#pragma once

#include <linux/mm.h>
#include <asm/tlbflush.h>
#include <asm/cacheflush.h>

/*
 * struct mmu_gather is an opaque type used by the mm code for passing around
 * any data needed by arch specific code for tlb_remove_page.
 */
struct mmu_gather
{
    unsigned long start;
    unsigned long end;

    /*
     * Do we have pending delayed rmap removals?
     */
    unsigned int delayed_rmap : 1;

    /*
     * at which levels have we cleared entries?
     */
    unsigned int cleared_ptes : 1;
    unsigned int cleared_pmds : 1;
    unsigned int cleared_puds : 1;
    unsigned int cleared_p4ds : 1;
};

static inline void __tlb_adjust_range(struct mmu_gather *tlb,
                                      unsigned long address,
                                      unsigned int range_size)
{
    tlb->start = min(tlb->start, address);
    tlb->end = max(tlb->end, address + range_size);
}

#ifndef __tlb_remove_tlb_entry
static inline void __tlb_remove_tlb_entry(struct mmu_gather *tlb, pte_t *ptep,
                                          unsigned long address)
{
}
#endif

/*
 * tlb_flush_{pte|pmd|pud|p4d}_range() adjust the tlb->start and tlb->end,
 * and set corresponding cleared_*.
 */
static inline void tlb_flush_pte_range(struct mmu_gather *tlb,
                                       unsigned long address, unsigned long size)
{
    __tlb_adjust_range(tlb, address, size);
    tlb->cleared_ptes = 1;
}

/**
 * tlb_remove_tlb_entries - remember unmapping of multiple consecutive ptes for
 *			    later tlb invalidation.
 *
 * Similar to tlb_remove_tlb_entry(), but remember unmapping of multiple
 * consecutive ptes instead of only a single one.
 */
static inline void tlb_remove_tlb_entries(struct mmu_gather *tlb,
                                          pte_t *ptep, unsigned int nr,
                                          unsigned long address)
{
    tlb_flush_pte_range(tlb, address, PAGE_SIZE * nr);
    for (;;)
    {
        __tlb_remove_tlb_entry(tlb, ptep, address);
        if (--nr == 0)
            break;
        ptep++;
        address += PAGE_SIZE;
    }
}
